using System.Web.Mvc;
using System.Web.Routing;
using System.Web.Security;
using System.Linq;
using DotNetOpenId;
using DotNetOpenId.RelyingParty;
using MindMelder.Core;
using MindMelder.Core.DataInterfaces;
using SharpArch.Core.PersistenceSupport;
using SharpArch.Core.DomainModel;
using System.Collections.Generic;
using System;
using SharpArch.Web.NHibernate;
using NHibernate.Validator.Engine;
using System.Text;
using SharpArch.Web.CommonValidator;
using SharpArch.Core;

namespace MindMelder.Web.Controllers
{
    [HandleError]
    public class UsersController : Controller
    {
        public UsersController(IUserRepository userRepository) {
            Check.Require(userRepository != null, "userRepository may not be null");

            this.userRepository = userRepository;
		}

		#region [ CRUD Actions ]

		[Transaction]
        public ActionResult Index() {
            List<User> users = userRepository.GetAll();
            return View(users);
        }

        [Transaction]
        public ActionResult Show(int id) {
            User user = userRepository.Get(id);
			TempData["skillslink"] = user.FullName + "'s skills";
			if (User.Identity.IsAuthenticated) {
				if (user.OpenId == User.Identity.Name) {
					TempData["skillslink"] = "My skills";
					return RedirectToAction("MyProfile");
				}
			}
            return View(user);
        }

		public ActionResult Skills(int id)
		{
			User user = userRepository.Get(id);
			TempData["heading"] = user.FullName + "'s skills";
			TempData["backlink"] = "Back to " + user.FullName + "'s profile";
			bool loggedOnUser = User.Identity.IsAuthenticated && User.Identity.Name == user.OpenId;
			if (loggedOnUser)
			{
				TempData["heading"] = "My skills";
				TempData["backlink"] = "Back to my profile";
				TempData["loggedonuser"] = "true";
			}
			else
			{
				TempData["loggedonuser"] = "false";
			}
			return View(userRepository.Get(id));
		}

		[ValidateAntiForgeryToken]
		[Transaction]
		[AcceptVerbs(HttpVerbs.Post)]
		[Authorize]
		public ActionResult Skills(int id, Skill skill) {
			if (ModelState.IsValid) {
				User user = userRepository.Get(id);
				if (user.OpenId != User.Identity.Name) // Trying to add skill to another user.
				{
					return View();
				}
				user.Skills.Add(skill);
				userRepository.SaveOrUpdate(user);

				TempData["skillmessage"] = "The skill was successfully added.";
				return Skills(id);
			}

			return View();
		}

		[Transaction]
		[Authorize]
		public ActionResult SendMessage(int id)
		{
			User user = userRepository.Get(id);
			if (User.Identity.Name == user.OpenId)
			{
				return RedirectToAction("Index");
			}
			return View(user);
		}

		[Transaction]
		[Authorize]
		public ActionResult Messages()
		{
			User user = userRepository.GetByOpenId(User.Identity.Name);
			foreach (var message in user.Messages)
			{
				message.SetRead();
			}
			userRepository.SaveOrUpdate(user);
			return View(user);
		}

		[Transaction]
		[AcceptVerbs(HttpVerbs.Post)]
		[Authorize]
		public ActionResult SendMessage(int id, string message)
		{
			User user = userRepository.Get(id);
			User currentUser = userRepository.GetByOpenId(User.Identity.Name);
            user.Messages.Add(new Message(currentUser, message, false));
			userRepository.SaveOrUpdate(user);
			TempData["message"] =
				"Your message has been sent and " + user.FullName + " will be notified the next time they visit the site.";
			return SendMessage(id);
		}

		[Transaction]
		[AcceptVerbs(HttpVerbs.Delete)]
		[Authorize]
		public void RemoveSkill(int id, string skillName)
		{
			User user = userRepository.Get(id);
			var skill = user.Skills.Where(s => s.Name == skillName).FirstOrDefault();
			if (skill != null)
			{
				user.Skills.Remove(skill);
				userRepository.SaveOrUpdate(user);
			}
		}

		public ActionResult ShowUsersWithSkill(string skill) {
			List<User> users = userRepository.FindUsersWithSkillsIn(skill);
			TempData["message"] = "Users with skills in " + skill;
			return View("Index", users);
		}
        
		[Authorize]
		public ActionResult MyProfile()
		{
			if (!User.Identity.IsAuthenticated)
				return RedirectToAction("Login");
			User currentUser = userRepository.FindOne(new Dictionary<string, object> {{"OpenId", User.Identity.Name}});
			if (currentUser != null)
			{
				return View(currentUser);
			}
			return RedirectToAction("Index", "Home");
		}

		[Authorize]
        public ActionResult Create() {
            return View();
        }

        [ValidateAntiForgeryToken]
        [Transaction]
        [AcceptVerbs(HttpVerbs.Post)]
		[Authorize]
        public ActionResult Create(User user) {
            if (ModelState.IsValid) {
                userRepository.SaveOrUpdate(user);

                TempData["message"] = "The user was successfully created.";
                return RedirectToAction("Index");
            }

            return View();
        }

        [Transaction]
		[Authorize]
        public ActionResult Edit()
		{
        	User user = userRepository.GetByOpenId(User.Identity.Name);
            return View(user);
        }

        [ValidateAntiForgeryToken]
        [Transaction]
        [AcceptVerbs(HttpVerbs.Post)]
		[Authorize]
        public ActionResult Edit(int id, [ModelBinder(typeof(DefaultModelBinder))] User user) {
            User userToUpdate = userRepository.Get(id);
            TransferFormValuesTo(userToUpdate, user);

            if (userToUpdate.IsValid()) {
                TransferFormValuesTo(userToUpdate, user);

                TempData["message"] = "The user was successfully updated.";
                return RedirectToAction("Index");
            }
            userRepository.DbContext.RollbackTransaction();
            MvcValidationAdapter.TransferValidationMessagesTo(ViewData.ModelState, 
                userToUpdate.ValidationResults());
            return View(userToUpdate);
        }

        private void TransferFormValuesTo(User userToUpdate, User userFromForm) {
			userToUpdate.FirstName = userFromForm.FirstName;
			userToUpdate.LastName = userFromForm.LastName;
			userToUpdate.Country = userFromForm.Country;
			userToUpdate.State = userFromForm.State;
			userToUpdate.BirthDate = userFromForm.BirthDate;
			userToUpdate.OpenId = userFromForm.OpenId;
        }

		#endregion

		#region [ Authentication Actions ]
		public ActionResult Login() {
			return View();
		}

		public ActionResult Logout() {
			FormsAuthentication.SignOut();
			return Redirect("/Home");
		}

		public ActionResult Authenticate() {
			var openid = new OpenIdRelyingParty();
			if (openid.Response == null) {
				// Stage 2: user submitting Identifier
				Identifier id;
				if (Identifier.TryParse(Request.Form["openid_identifier"], out id)) {
					try {
						openid.CreateRequest(Request.Form["openid_identifier"]).RedirectToProvider();
					}
					catch {
						ViewData["Message"] = "The provider could not be found.";
						return View("Login");
					}
				}
				else {
					ViewData["Message"] = "An error occured. Please try again.";
					return View("Login");
				}
			}
			else {
				// Stage 3: OpenID Provider sending assertion response
				switch (openid.Response.Status) {
					case AuthenticationStatus.Authenticated:
						var userExists = userRepository.FindOne(new Dictionary<string, object> { { "OpenId", openid.Response.ClaimedIdentifier.ToString() } });
						if (userExists == null)
						{
							var newUser = new User();
							newUser.OpenId = openid.Response.ClaimedIdentifier.ToString();
							newUser.FirstName = "(unknown)";
							userRepository.SaveOrUpdate(newUser);
						}
						FormsAuthentication.RedirectFromLoginPage(openid.Response.ClaimedIdentifier, false);
						break;
					case AuthenticationStatus.Canceled:
						ViewData["Message"] = "Canceled at provider";
						return View("Login");
					case AuthenticationStatus.Failed:
						ViewData["Message"] = openid.Response.Exception.Message;
						return View("Login");
				}
			}
			return new EmptyResult();
		}
		#endregion

		private readonly IUserRepository userRepository;

    }
}
